﻿/* 
 * Copyright (c) Intel Corporation
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * -- Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * -- Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * -- Neither the name of the Intel Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived from
 *    this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE INTEL OR ITS
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using ExtensionLoader;
using OpenMetaverse;
using OpenMetaverse.StructuredData;
using CableBeachMessages;

namespace WorldServer.Extensions
{
    public class SimpleAttributes : IExtension<WorldServer>, IAttributeProvider
    {
        const string ATTRIBUTES_FILE = WorldServer.DATA_DIR + "WorldServer.SimpleAttributes.txt";

        WorldServer server;
        Dictionary<Uri, Dictionary<Uri, OSD>> attributeStorage = new Dictionary<Uri, Dictionary<Uri, OSD>>();
        Dictionary<UUID, Uri> uuidMap = new Dictionary<UUID, Uri>();

        public SimpleAttributes()
        {
        }

        public bool Start(WorldServer server)
        {
            this.server = server;

            try
            {
                OSDMap map = OSDParser.DeserializeJson(new FileStream(ATTRIBUTES_FILE, FileMode.Open, FileAccess.Read)) as OSDMap;
                if (map != null) Deserialize(map);
            }
            catch (Exception) { }

            return true;
        }

        public void Stop()
        {
            StringBuilder sb = new StringBuilder();
            LitJson.JsonWriter writer = new LitJson.JsonWriter(sb);
            writer.IndentValue = 4;
            writer.PrettyPrint = true;
            OSDParser.SerializeJsonString(Serialize(), ref writer);

            File.WriteAllText(ATTRIBUTES_FILE, sb.ToString());
        }

        public Uri UUIDToIdentity(UUID avatarID)
        {
            Uri identity = null;
            uuidMap.TryGetValue(avatarID, out identity);
            return identity;
        }

        public Dictionary<Uri, OSD> GetAttributes(Uri identity)
        {
            if (identity == null)
                return null;

            Dictionary<Uri, OSD> attributes;
            if (!attributeStorage.TryGetValue(identity, out attributes))
            {
                Logger.Debug("[SimpleAttributes] Creating new attributes for " + identity);
                attributes = new Dictionary<Uri, OSD>();
                attributes[AvatarAttributes.AVATAR_ID] = OSD.FromUUID(CableBeachUtils.IdentityToUUID(identity));
            }

            return attributes;
        }

        public void UpdateAttributes(Uri identity, Dictionary<Uri, OSD> attributes)
        {
            lock (attributeStorage)
            {
                attributeStorage[identity] = attributes;
                uuidMap[CableBeachUtils.IdentityToUUID(identity)] = identity;
            }
        }

        #region Persistence

        OSDMap Serialize()
        {
            lock (attributeStorage)
            {
                OSDMap map = new OSDMap();

                foreach (KeyValuePair<Uri, Dictionary<Uri, OSD>> entry in attributeStorage)
                {
                    OSDMap attributes = new OSDMap(entry.Value.Count);

                    foreach (KeyValuePair<Uri, OSD> attributeEntry in entry.Value)
                        attributes.Add(attributeEntry.Key.ToString(), attributeEntry.Value);

                    map.Add(entry.Key.ToString(), attributes);
                }

                return map;
            }
        }

        void Deserialize(OSDMap map)
        {
            int attributeCount = 0;

            lock (attributeStorage)
            {
                attributeStorage.Clear();

                foreach (KeyValuePair<string, OSDMap> entry in map)
                {
                    Dictionary<Uri, OSD> attributes = new Dictionary<Uri, OSD>(entry.Value.Count);

                    foreach (KeyValuePair<string, OSD> attributeEntry in entry.Value)
                        attributes.Add(new Uri(attributeEntry.Key), attributeEntry.Value);

                    Uri identity = new Uri(entry.Key);
                    attributeStorage.Add(identity, attributes);
                    uuidMap[CableBeachUtils.IdentityToUUID(identity)] = identity;
                    ++attributeCount;
                }
            }

            Logger.Info("[SimpleAttributes] Loaded " + attributeCount + " attributes across " + attributeStorage.Count + " identities");
        }

        #endregion Persistence
    }
}
